// -----------------------------------------------------------------------------------
//  NKDBarcodeOffscreenView.m
// -----------------------------------------------------------------------------------
//  Created by Jeff LaMarche on Mon May 06 2002.
//  �2002 Naked Software. All rights reserved.
// -----------------------------------------------------------------------------------
// THIS	SOURCE CODE IS PROVIDED AS-IS WITH NO WARRANTY OF ANY KIND
// -----------------------------------------------------------------------------------
// You may use and redistribute this source code without limitation
// -----------------------------------------------------------------------------------
#import "NKDBarcodeOffscreenView.h"
#import <CoreText/CoreText.h>

#define captionHeightSpace 0.0
#define captionWidthSpace 0.0
#define captionFontAdjust 1.5
#define captionYPosAdjust 3.0
#define captionLeftCaptionXPosAdjustRate 0.25
#define captionUPECContainerAdjustRage 2.0
#define captionOtherContainerAdjustRate 1.0
#define captionKerningRate 1.3
#define captionXPosAdjust 5.0
#define captionXPosKerningAdjustRate 3.0
#define captionNonLeftKerningAdjustRate 2.0
#define captionRightCaptionXPosAdjustRate 0.07

@interface NKDBarcodeOffscreenView()

/*!
    @function
    @abstract   Get the screen size of attritubed string on context
    @discussion Get the screen size of attritubed string on context
    @param      the attritubed string, CGContext
    @result     size in CGSize
	@author		Daniel Lu @ June 09, 2011
*/
- (CGSize) getAttritubedStringSize: (NSAttributedString *) str onContext: (CGContextRef) context;

@end


@implementation NKDBarcodeOffscreenView

- (id)initWithBarcode:(NKDBarcode *)inBarcode
{
    CGRect	frame = CGRectMake(0,0,[inBarcode width], [inBarcode height]);
    // Calculate frame and then...

    
    self = [super initWithFrame:frame];
    if (self)
    {
        [self setBarcode:inBarcode];
    }
    return self;
}

// A more elegant solution offered by Sato Akira (arigoto!)
- (void)drawRect:(CGRect)rect
{
	CGContextRef context = UIGraphicsGetCurrentContext();
	CGContextSetAllowsAntialiasing(context, false);
	CGContextSetInterpolationQuality(context, kCGInterpolationNone);
	
	//flip the coordinate system
	CGContextSetTextMatrix(context, CGAffineTransformIdentity);
	CGContextTranslateCTM(context, 0, self.bounds.size.height);
	CGContextScaleCTM(context, 1.0, -1.0);
	
	int				i, barCount=0;
    float			curPos = [barcode firstBar];
    NSString 		*codeString = [barcode completeBarcode];

    BOOL			started = NO;
    int				lastBarIndex = -1;

    for (i = 0; i < [codeString length]; i++)
    {
        if ([codeString characterAtIndex:i] == '1')
        {
            if (!started)
                started = YES;

            barCount++;
            lastBarIndex = i;

            // If last character is a bar, it needs to be printed here.
            if (i == [codeString length]-1)
            {
				CGContextSetLineWidth(context, 0.0);
				CGMutablePathRef path = CGPathCreateMutable();
				CGPathAddRect(path, NULL,  CGRectMake(curPos,
													  [barcode barBottom:lastBarIndex],
													  [barcode barWidth] * barCount,
													  [barcode barTop:lastBarIndex] - [barcode barBottom:lastBarIndex]));
/*				CGContextFillRect(context, CGRectMake(curPos,
													  [barcode barBottom:lastBarIndex],
													  [barcode barWidth] * barCount,
													   [barcode barTop:lastBarIndex] - [barcode barBottom:lastBarIndex])); */
				CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
				CGContextAddPath(context, path);
				CGContextDrawPath(context, kCGPathFill);
				CGPathRelease(path);
				CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
            }
        }
        else
        {
            if (started)
            {
				CGContextSetLineWidth(context, 0.0);
				CGMutablePathRef path = CGPathCreateMutable();
				/*
				CGContextFillRect(context, CGRectMake(curPos,
													  [barcode barBottom:lastBarIndex],
													  [barcode barWidth] * barCount,
													  [barcode barTop:lastBarIndex] - [barcode barBottom:lastBarIndex] ));
				 */
				CGPathAddRect(path, NULL, CGRectMake(curPos,
													 [barcode barBottom:lastBarIndex],
													 [barcode barWidth] * barCount,
													 [barcode barTop:lastBarIndex] - [barcode barBottom:lastBarIndex] ));
				CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);	
				CGContextAddPath(context, path);
				CGContextDrawPath(context, kCGPathFill);
				CGPathRelease(path);
				CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); 

            }
            curPos += [barcode barWidth] * (barCount + 1);
            barCount = 0;
            started = NO;
        }
    }
    if ([barcode printsCaption])
    {
		/**
		 * This has been fixed by Daniel Lu with Core Text @ June 09, 2011
		 */
		float kerning;
		
		//CGSize captionSize;
		CGRect bBounds = [self bounds];
		
		float heightSpace = captionHeightSpace;
		float widthSpace = captionWidthSpace;
		float barWidth = [barcode barWidth];
		
		NSString *leftCaption = [barcode leftCaption];
		NSString *caption = [barcode caption];
		NSString *rightCaption = [barcode rightCaption];
		
		float yPos = bBounds.origin.y - heightSpace;
		
		NSMutableAttributedString *sAttr;
		
		NSRange range;

//		UIFont *font = [UIFont systemFontOfSize: [barcode fontSize]];
		CTFontRef fontStyle = CTFontCreateUIFontForLanguage(kCTFontSystemFontType, [barcode fontSize] * captionFontAdjust, NULL);
		
		//Generate the left alignment style
		CTTextAlignment leftAlignment = kCTLeftTextAlignment;
		CTParagraphStyleSetting leftAlignmentSetting;
		leftAlignmentSetting.spec = kCTParagraphStyleSpecifierAlignment;
		leftAlignmentSetting.valueSize = sizeof(leftAlignmentSetting);
		leftAlignmentSetting.value = &leftAlignment;
		
		CTParagraphStyleRef leftAlignmentStyle = CTParagraphStyleCreate(&leftAlignmentSetting, 1);
		
		//Draw the left caption
		if ((nil != leftCaption) && (NO == [leftCaption isEqualToString: @""])) {
			/*
			sAttr = [[NSAttributedString allocWithZone: [self zone]] initWithString: leftCaption attributes: [NSDictionary dictionaryWithObjectsAndKeys: font, (id) kCTFontAttributeName,
																													 leftAlignmentStyle, (id) kCTParagraphStyleAttributeName,
																													 [NSNumber numberWithFloat: kerning], (id) kCTKernAttributeName, nil]];
			*/
			sAttr = [[NSMutableAttributedString allocWithZone: [self zone]] 
					 initWithString: leftCaption];
			
			range = NSMakeRange(0, [sAttr length]);//NSRangeFromString(leftCaption);
			
			
			[sAttr addAttribute: (id) kCTFontAttributeName 
						  value: (id) fontStyle 
						  range: range];
			
			
			[sAttr addAttribute: (id) kCTParagraphStyleAttributeName 
						  value: (id) leftAlignmentStyle
						  range: range];
			
			[sAttr addAttribute: (id) kCTKernAttributeName 
						  value: [NSNumber numberWithFloat: kerning] 
						  range: range];

			CTLineRef leftCaptionLine = CTLineCreateWithAttributedString((CFAttributedStringRef) sAttr);
			CGContextSetTextPosition(context, (bBounds.origin.x + widthSpace + 
											   captionLeftCaptionXPosAdjustRate * [barcode firstBar]), 
									 yPos + captionYPosAdjust);
			CTLineDraw(leftCaptionLine, context);
			
			[sAttr release];
			CFRelease(leftCaptionLine);
		}
		
		if ((nil != caption) && (NO == [caption isEqualToString: @""]) ) {
			unsigned int cCount, captionLength;
			CGSize captionSize;
			float container;
			NSArray *captions = [caption componentsSeparatedByString: @"\t"];
			
			if (((nil != leftCaption) && (NO == [leftCaption isEqualToString: @""])) || (1 < [captions count])) {
				if (1 == [captions count])  //For UPCE
				{
					container = [barcode width] / (float) [captions count] - 
					captionUPECContainerAdjustRage * [[barcode initiator] length] * barWidth - 
					captionUPECContainerAdjustRage * [barcode firstBar];
				} else {
					container = [barcode width] / (float) [captions count] - 
					captionOtherContainerAdjustRate * [[barcode initiator] length] * barWidth - 
					captionOtherContainerAdjustRate * [barcode firstBar];
				}
				
				for (cCount = 0; cCount < [captions count]; cCount ++ ) {
					kerning = 0.0;
					NSString *currentString = (NSString *) [captions objectAtIndex: cCount];
					
					captionLength = [currentString length];
					
					//NSRange currentRange = NSRangeFromString(currentString); //NSMakeRange(0, captionLength);
					range = NSMakeRange(0, captionLength);
					
					sAttr = [[NSMutableAttributedString allocWithZone: [self zone]] initWithString: currentString];
					
					[sAttr addAttribute: (id) kCTFontAttributeName 
								  value: (id) fontStyle 
								  range: range];
					 
					[sAttr addAttribute: (id) kCTParagraphStyleAttributeName 
								  value: (id) leftAlignmentStyle 
								  range: range];
					 
					[sAttr addAttribute: (id) kCTKernAttributeName 
								   value: [NSNumber numberWithFloat: kerning] 
								   range: range];
					
					captionSize = [self getAttritubedStringSize: sAttr onContext: context];
					
					kerning = (container - captionSize.width) / (float)(2 * captionLength);
					 
					[sAttr removeAttribute: (id) kCTKernAttributeName range: range];
					
					[sAttr addAttribute: (id) kCTKernAttributeName 
								  value: [NSNumber numberWithFloat: captionKerningRate * kerning] 
								  range: range];
					 
					CTLineRef captionLine = CTLineCreateWithAttributedString( (CFAttributedStringRef) sAttr);
					CGContextSetTextPosition(context, bBounds.origin.x - captionXPosAdjust + 
											 widthSpace + kerning * captionXPosKerningAdjustRate + 
											 [barcode firstBar] + 
											 (float)(cCount + 1) * [[barcode initiator] length] * barWidth + 
											 (float)cCount * container,
											 yPos + captionYPosAdjust);
					CTLineDraw(captionLine, context);
					 
					[sAttr release];
					CFRelease(captionLine);
				} 
			} else {
				cCount = 0;
				kerning = 0.0;
				sAttr = [[NSMutableAttributedString allocWithZone:[self zone]] initWithString:[captions objectAtIndex:cCount]];

				captionLength = [(NSString *)[captions objectAtIndex:cCount] length];
                range = NSMakeRange(0,captionLength);
				container = [barcode width];

				[sAttr addAttribute: (id) kCTFontAttributeName 
							  value: (id) fontStyle 
							  range: range];
				
				[sAttr addAttribute: (id) kCTParagraphStyleAttributeName 
							  value: (id) leftAlignmentStyle 
							  range: range];
				
				[sAttr addAttribute: (id) kCTKernAttributeName 
							  value: [NSNumber numberWithFloat: kerning] 
							  range: range];
				
				captionSize = [self getAttritubedStringSize: sAttr onContext: context];
				
				kerning = (container - captionSize.width) / (float)(2 * captionLength);
				
				[sAttr removeAttribute: (id) kCTKernAttributeName range: range];
				[sAttr addAttribute: (id) kCTKernAttributeName 
							  value: [NSNumber numberWithFloat: captionNonLeftKerningAdjustRate * kerning] 
							  range: range];
				
				CTLineRef captionLine = CTLineCreateWithAttributedString( (CFAttributedStringRef) sAttr);
				CGContextSetTextPosition(context, bBounds.origin.x + widthSpace + kerning, 
										 yPos + captionYPosAdjust);
				CTLineDraw(captionLine, context);
				
				[sAttr release];
				CFRelease(captionLine);
			}
		}
		
		// Right caption for UPC / EAN
		if ((nil != rightCaption) && (NO == [rightCaption isEqualToString:@""])) {
			kerning = 0.0;
			sAttr = [[NSMutableAttributedString allocWithZone:[self zone]] initWithString:rightCaption];
			range = NSMakeRange(0, [sAttr length]);
			
			[sAttr addAttribute: (id) kCTFontAttributeName
						  value: (id) fontStyle
						  range: range];
			
			[sAttr addAttribute: (id) kCTParagraphStyleAttributeName 
						  value: (id) leftAlignmentStyle 
						  range: range];
			
			[sAttr addAttribute: (id) kCTKernAttributeName 
						  value: [NSNumber numberWithFloat: kerning] 
						  range: range];
			
			CTLineRef rightCaptionLine = CTLineCreateWithAttributedString( (CFAttributedStringRef) sAttr);
			CGContextSetTextPosition(context,  bBounds.origin.x + widthSpace + 
									 [barcode lastBar] + ([barcode width] * captionRightCaptionXPosAdjustRate), 
									 yPos + captionYPosAdjust);
			CTLineDraw(rightCaptionLine, context);
			
			[sAttr release];
			CFRelease(rightCaptionLine);
		}
		
		CFRelease(fontStyle);
		CFRelease(leftAlignmentStyle);
    }
}
// -----------------------------------------------------------------------------------
-(NKDBarcode *)barcode
// -----------------------------------------------------------------------------------
{
    return barcode;
}
// -----------------------------------------------------------------------------------
-(void)setBarcode:(NKDBarcode *)inBarcode
// -----------------------------------------------------------------------------------
{
    [barcode autorelease];
    barcode = inBarcode;
}
// -----------------------------------------------------------------------------------
-(BOOL)knowsPageRange:(NSRange *)rptr
// -----------------------------------------------------------------------------------
{
    rptr->location = 1;
    rptr->length = 1;
    return YES;
}
// -----------------------------------------------------------------------------------
-(CGRect)rectForPage:(int)pageNum
// -----------------------------------------------------------------------------------
{
    return  CGRectMake(0,0,[[self barcode] width], [[self barcode] height]);
}
- (UIImage *)_renderViewAsImage:(CGRect) rect
    /* this method is not intended to be called directly */
/* used by dataWithPDFInsideRect: when multithreading is required */
{
	UIGraphicsBeginImageContext(CGSizeMake(rect.size.width, rect.size.height));
	[self drawRect:rect];
	UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
	UIGraphicsEndImageContext();
	return img;
}

- (UIImage *)imageInsideRect:(CGRect)rect
    /* This method overrides the default NSView version to allow the view 
    to be rendered to PDF within another print operation. If there is 
    already a print operation running on the current thread, this method 
    spawns a new thread to render the PDF, otherwise the default NSView 
 version of dataWithPDFInsideRect: is called... */
{
	UIImage * result = [self _renderViewAsImage:rect];
	return result;
}

- (NSData *)pdfInsideRect:(CGRect)rect {
	NSMutableData * data = [NSMutableData data];
	CGDataConsumerRef consumer = CGDataConsumerCreateWithCFData((CFMutableDataRef)data);
	CGRect media = CGRectMake(0.0, 0.0, rect.size.width, rect.size.height);
	CGContextRef context = CGPDFContextCreate(consumer, &media, (CFDictionaryRef)[NSDictionary dictionary]);
	UIGraphicsPushContext(context);
	CGPDFContextBeginPage(context, (CFDictionaryRef)[NSDictionary dictionary]);
	[self drawRect:rect];
	UIGraphicsPopContext();
	CGPDFContextEndPage(context);
	CGPDFContextClose(context);
	CGDataConsumerRelease(consumer);
	CGContextRelease(context);
	return data;
}

- (CGSize) getAttritubedStringSize: (NSAttributedString *) str onContext: (CGContextRef) context
{
	CTLineRef textLine = CTLineCreateWithAttributedString( (CFAttributedStringRef) str);
	CGRect textRect = CTLineGetImageBounds(textLine, context);
	CFRelease(textLine);
	return textRect.size;
}



@end
